# -*- coding: utf-8 -*-
import os
import sys
import logging
import wsgiref.handlers
from werkzeug import script, Client, Response
from werkzeug.contrib import profiler
from . import make_application

class nul:
    write = staticmethod(lambda s: None)


def runcgi(application):
    def action( hostname            =   ('h', ''),
                port                =   ('p', 3001),
                protocol            =   'scgi',
                method              =   'threaded',
                children            =   50,
                daemonize           =   False,
                pidfile             =   '/var/run/accost.pid',
                workdir             =   '.',
                outlog              =   '/dev/null',
                errlog              =   '/dev/null',
                umask               =   022,
                user                =   '',
        ):
        """run application use flup
        you can choose these arguments:
        protocol :   scgi, fcgi or ajp
        method   :   threaded or fork
        children :   number of threads or processes"""
        if protocol not in ('scgi', 'fcgi', 'ajp'):
            sys.exit('invalid protocol: %s'%protocol)
        flup_module = 'server.%s'%protocol

        kw = {'maxSpare': children, 'minSpare': children}

        if method == 'threaded':
            kw['maxThreads'] = children
        elif method == 'fork':
            flup_module += '_fork'
            kw['maxChildren'] = children
        else:
            sys.exit('invalid method: %s'%method)

        if protocol == "scgi":
            kw['loggingLevel'] = logging.DEBUG

        try:
            WSGIServer = getattr(__import__('flup.' + flup_module, '', '', flup_module), 'WSGIServer')
        except ImportError:
            print "You need to install flup"
            sys.exit()

        if user:
            try:
                import pwd
                user = pwd.getpwnam(user)
            except KeyError:
                sys.exit("can not find user: %s"%user)

        if os.name!='posix' and outlog=='/dev/null':
            outlog = None
        if os.name!='posix' and errlog=='/dev/null':
            errlog = None

        if daemonize:
            from .utils.daemonize import become_daemon
            become_daemon(workdir, outlog, errlog, umask)

        if daemonize and pidfile and os.name=='posix':
            try:
                fp = open(pidfile, 'w')
                fp.write("%d\n" % os.getpid())
                fp.close()
            except:
                pass

        if user:
            os.setegid(user.pw_gid)
            os.seteuid(user.pw_uid)

        WSGIServer(application, bindAddress=(hostname, port), **kw).run()

    return action


def run_cherrypy_server(application):
    def action( hostname            =   ('h', ''),
                port                =   ('p', 8000),
                threads             =   50,
                server_name         =   '0.0.0.0',
                max                 =   -1,
                request_queue_size  =   5,
                timeout             =   10,
                daemonize           =   False,
                pidfile             =   '/var/run/accost-cherrypy.pid',
                workdir             =   '.',
                outlog              =   '/dev/null',
                errlog              =   '/dev/null',
                umask               =   022,
        ):
        """run application use cherrypy http server"""
        if os.name!='posix' and outlog=='/dev/null':
            outlog = None
        if os.name!='posix' and errlog=='/dev/null':
            errlog = None

        if daemonize:
            from .utils.daemonize import become_daemon
            become_daemon(workdir, outlog, errlog, umask)

        if daemonize and pidfile and os.name=='posix':
            try:
                fp = open(pidfile, 'w')
                fp.write("%d\n" % os.getpid())
                fp.close()
            except:
                pass


        from .wsgiserver import CherryPyWSGIServer
        server = CherryPyWSGIServer((hostname, port), application,
                threads, server_name, max, request_queue_size, timeout)
        try:
            server.start()
        except KeyboardInterrupt:
            server.stop()

    return action


def run_asynwsgi_server(application):
    def action(hostname=('h', ''), port=('p', 8000), server_name='0.0.0.0'):
        """run application use asynwsgi http server"""
        from .asynwsgi import WSGIServer
        server = WSGIServer((hostname, port), application, server_name)
        server.start()

    return action


def run_tornado_server(application):
    def action(hostname=('h', ''), port=('p', 8000), server_name='0.0.0.0'):
        """run application use tornado http server"""
        from tornado.wsgi import WSGIContainer
        from tornado.httpserver import HTTPServer
        from tornado.ioloop import IOLoop
        container = WSGIContainer(application)
        server = HTTPServer(container)
        server.listen(port)
        try:
            IOLoop.instance().start()
        except KeyboardInterrupt:
            IOLoop.instance().stop()

    return action


def show_tables(app):
    def action():
        """show tables of all application databases"""
        for dbname, store in app.accost_application.db.items():
            print '[%s]'%dbname
            store.showTables()
        engine = getattr(app.accost_application.app, "engine")
        if not engine: return
        conn = engine.connect()
        for r in conn.execute("SHOW TABLES").fetchall():
            print r[0]

    return action


def show_create(app):
    def action():
        """show create table sql of all application databases"""
        for dbname, store in app.accost_application.db.items():
            print '[%s]'%dbname
            store.showCreate()
        engine = getattr(app.accost_application.app, "engine")
        if not engine: return
        conn = engine.connect()
        for r in conn.execute("SHOW TABLES").fetchall():
            for line in conn.execute("SHOW CREATE TABLE `%s`"%r[0]):
                print line[1]
                print

    return action


def dbshell(app):
    def action():
        """run mysql client use database configuration of the application"""
        db = app.accost_application.db
        if len(db) == 0:
            sys.exit('database not found')
        elif len(db) == 1:
            db.popitem()[1].runDBShell()
        else:
            print 'There are %d databases, please choose one of them:'%len(db.items())
            name = input(', '.join([dbname for dbname in db]))
            if not name in db:
                sys.exit('no such database')
            db[name].runDBShell()

    return action


def http_get(app):
    def action(url="/"):
        """make a request use GET method"""
        c = Client(app, Response)
        resp = c.get(url)
        print resp.data

    return action


def run_eurasia_server(app):
    def action(hostname=('h', '0.0.0.0'), port=('p', 8000)):
        """run application use eurasia http server"""
        try:
            from eurasia import WSGIServer
        except ImportError:
            print "You need to install eurasia"
            sys.exit()
        server = WSGIServer(app, bindAddress=(hostname, port))
        server.run()

    return action


def run_eventlet_server(app):
    def action(hostname=('h', '0.0.0.0'), port=('p', 8000), debug=False):
        """run application use eventlet http server"""
        try:
            import eventlet
            from eventlet import wsgi
        except ImportError:
            print "You need to install eventlet"
            sys.exit()
        if debug:
            log = None
        else:
            log = nul
        wsgi.server(eventlet.listen((hostname, port)), app, log=log)

    return action


def run_twisted_server(app):
    def action(port=('p', 8000), reactor_type=('r', 0)):
        """run application use twisted http server
        @reactor_type: [default 0]
            1       epoll   reactor
            2       poll    reactor
            3       kqueue  reactor
            4       iocp    reactor
            other   select  reactor
        """
        try:
            if reactor_type == 1:
                from twisted.internet import epollreactor
                epollreactor.install()
            elif reactor_type == 2:
                from twisted.internet import pollreactor
                pollreactor.install()
            elif reactor_type == 3:
                from twisted.internet import kqreactor
                kqreactor.install()
            elif reactor_type == 4:
                from twisted.internet import iocpreactor
                iocpreactor.install()
            from twisted.internet import reactor
            from twisted.application import service, strports
            from twisted.web2 import server, channel, wsgi
        except ImportError:
            print "You need to install twisted and twisted.web2"
            sys.exit()

        application = service.Application('web2-wsgi') # call this anything you like
        site = server.Site(wsgi.WSGIResource(app))
        s = strports.service('tcp:%d'%port, channel.HTTPFactory(site))
        s.setServiceParent(application)
        s.startService()
        reactor.run()

    return action


def clean(dir=('d', '.'), postfix=('x', 'pyc'), remove=False):
    """remove the files with specify postfix"""
    check = lambda s: s.endswith(".%s"%postfix)
    for dirpath, dirnames, filenames in os.walk(dir):
        for filename in filter(check, filenames):
            p = os.path.abspath(os.path.join(dirpath, filename))
            if remove:
                print "remove %s"%p
                os.remove(p)
            else:
                print p


def show_urls(app):
    def action():
        """display url structure"""
        for rule in app.accost_application.url_mapping.iter_rules():
            print "%-30s"%rule, rule.endpoint

    return action


def winsvc(app):
    def action():
        """handler windows service"""
        import win32serviceutil
        import win32service
        import win32event
        from .wsgiserver import CherryPyWSGIServer

        class Service(win32serviceutil.ServiceFramework):
            _svc_name_ = "accost_service"
            _svc_display_name_ = "Accost Service"
            def __init__(self, args):
                win32serviceutil.ServiceFramework.__init__(self, args)
                self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

            def SvcDoRun(self):
                self.server = CherryPyWSGIServer(('0.0.0.0', 80), app, 50)
                self.server.start()
                # 等待服务被停止
                win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)

            def SvcStop(self):
                # 先告诉SCM停止这个过程
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                # 停止服务
                self.server.stop()
                # 设置事件
                win32event.SetEvent(self.hWaitStop)

        win32serviceutil.HandleCommandLine(Service, argv=sys.argv[1:])

    return action


def bshell(app):
    def action():
        from bpython import embed
        embed({"app": app})

    return action


class NewCommand(object):
    def __init__(self, app):
        self.app = app
        self.application = application = make_application(app)
        self._actions = {
                'runcgi'    :   runcgi(application),
                'shell'     :   script.make_shell(lambda: {"app": application},
                                    "Interactive Accost Shell"),
                'runserver' :   script.make_runserver(lambda: application,
                                    use_reloader=True, threaded=False, hostname='0.0.0.0',
                                    port=7777, use_debugger=True),
                'cherrypy'  :   run_cherrypy_server(application),
                'asynwsgi'  :   run_asynwsgi_server(application),
                'eurasia'   :   run_eurasia_server(application),
                'eventlet'  :   run_eventlet_server(application),
                'twisted'   :   run_twisted_server(application),
                'tornado'   :   run_tornado_server(application),
                'profile'   :   profiler.make_action(lambda: application, port=7777),
                'http_get'  :   http_get(application),
                'clean'     :   clean,
                'winsvc'    :   winsvc(application),
                'bshell'    :   bshell(application),
                'show_create':  show_create(application),
                'show_tables':  show_tables(application),
                'dbshell'   :   dbshell(application),
                'show_urls' :   show_urls(application),
        }
        for name, action in getattr(app, "actions", []):
            self._actions[name] = action(application)

    def run(self):
        script.run(self._actions, '')

    def register(self, func, name=None):
        name = name if name is not None else func.func_name
        self._actions[name] = func


# 以下为使用argparse重新写的命令行界面
def add_daemon_arguments(parser):
    parser.add_argument("--daemonize", default=False, action="store_true", dest="daemonize", help="run as a daemon")
    parser.add_argument("--pidfile", default="", metavar="PATH", dest="pidfile", help="pidfile to use if daemonized, only works when daemonized")
    parser.add_argument("--workdir", default=".", metavar="DIR", dest="workdir", help="only works when daemonized")
    parser.add_argument("--outlog", default=None, dest="outlog", help="only works when daemonized")
    parser.add_argument("--errlog", default=None, dest="errlog", help="only works when daemonized")
    parser.add_argument("--umask", default=022, type=int, dest="umask", help="only works when daemonized")
    parser.add_argument("--user", default=None, dest="user", help="only works when it is run by root user")


def add_host_arguments(parser):
    parser.add_argument("--host", default="0.0.0.0", metavar="HOST", dest="hostname", help="hostname to bind (default 0.0.0.0)")
    parser.add_argument("-p", "--port", default=7777, type=int, metavar="PORT", dest="port", help="server port (default 7777)")
    parser.add_argument("--server_name", default="0.0.0.0", metavar="NAME", dest="server_name")


def _process_daemon(args):
    user = None
    if args.user:
        try:
            import pwd
            user = pwd.getpwnam(args.user)
        except KeyError:
            sys.exit("can not find user: %s"%args.user)

    if args.daemonize:
        from .utils.daemonize import become_daemon
        outlog = args.outlog
        if os.name == "posix" and args.outlog is None:
            outlog = "/dev/null"
        errlog = args.errlog
        if os.name == "posix" and args.errlog is None:
            errlog = "/dev/null"
        become_daemon(args.workdir, outlog, errlog, args.umask)

    if args.daemonize and args.pidfile and os.name=='posix':
        try:
            fp = open(args.pidfile, 'w')
            fp.write("%d\n" % os.getpid())
            fp.close()
        except:
            pass

    if user:
        os.setegid(user.pw_gid)
        os.seteuid(user.pw_uid)


class cmd_runcgi(object):
    """run application use flup"""
    def __init__(self, parser):
        add_host_arguments(parser)
        parser.add_argument("--protocol", default="scgi", choices=["scgi", "fcgi", "ajp"], metavar="PROTOCOL", dest="protocol", help="scgi, fcgi or ajp(default scgi)")
        parser.add_argument("--method", default="threaded", choices=["threaded", "fork"], metavar="METHOD", dest="method", help="threaded or fork(default threaded)")
        parser.add_argument("--children", default=50, type=int, metavar="NUM", dest="children", help="number of threads or processes(default=50)")
        add_daemon_arguments(parser)

    def __call__(self, args, application):
        flup_module = 'server.%s'%args.protocol

        kw = {'maxSpare': args.children, 'minSpare': args.children}

        if args.method == 'threaded':
            kw['maxThreads'] = args.children
        elif args.method == 'fork':
            flup_module += '_fork'
            kw['maxChildren'] = args.children
        else:
            sys.exit('invalid method: %s'%args.method)

        if args.protocol == "scgi":
            kw['loggingLevel'] = logging.DEBUG

        try:
            WSGIServer = getattr(__import__('flup.' + flup_module, '', '', flup_module), 'WSGIServer')
        except ImportError:
            print "You need to install flup"
            sys.exit()

        _process_daemon(args)

        WSGIServer(application, bindAddress=(args.hostname, args.port), **kw).run()


class cmd_cherrypy(object):
    """run application use cherrypy http server"""
    def __init__(self, parser):
        add_host_arguments(parser)
        parser.add_argument("--threads", default=50, type=int, metavar="NUM", dest="threads", help="number of threads")
        parser.add_argument("--max", default=-1, type=int, metavar="NUM", dest="max", help="the maximum number of queued requests (defaults to -1 = no limit)")
        parser.add_argument("--request_queue_size", default=5, type=int, metavar="NUM", dest="request_queue_size", help="the 'backlog' argument to socket.listen(); specifies the maximum number of queued connections (default 5)")
        parser.add_argument("--timeout", default=10, type=int, metavar="SECONDS", dest="timeout", help="the timeout in seconds for accepted connections (default 10)")
        add_daemon_arguments(parser)

    def __call__(self, args, application):
        _process_daemon(args)

        from .wsgiserver import CherryPyWSGIServer
        server = CherryPyWSGIServer((args.hostname, args.port), application,
                args.threads, args.server_name, args.max, args.request_queue_size, args.timeout)
        try:
            server.start()
        except KeyboardInterrupt:
            server.stop()


class cmd_clean(object):
    """remove the files with specify postfix"""
    def __init__(self, parser):
        parser.add_argument("-d", "--dir", default=".", metavar="DIR", dest="dir", help="execute from the DIR")
        parser.add_argument("-x", "--postfix", default="pyc", metavar="POSTFIX", dest="postfix", help="file postfix to use")
        parser.add_argument("--remove", action="store_true", default=False, dest="remove", help="do real remove")

    def __call__(self, args, app):
        check = lambda s: s.endswith(".%s"%args.postfix)
        for dirpath, dirnames, filenames in os.walk(args.dir):
            for filename in filter(check, filenames):
                p = os.path.abspath(os.path.join(dirpath, filename))
                if args.remove:
                    print "remove %s"%p
                    os.remove(p)
                else:
                    print p


class cmd_asynwsgi(object):
    """run application use asynwsgi http server"""
    def __init__(self, parser):
        add_host_arguments(parser)
        add_daemon_arguments(parser)

    def __call__(self, args, application):
        _process_daemon(args)

        from .asynwsgi import WSGIServer
        server = WSGIServer((args.hostname, args.port), application, args.server_name)
        server.start()


class cmd_bshell(object):
    """bpython shell"""
    def __init__(self, parser):
        pass

    def __call__(self, args, app):
        from bpython import embed
        embed({"app": app})


class cmd_httpget(object):
    """make a request use GET method"""
    def __init__(self, parser):
        parser.add_argument("url", metavar="URL")
        add_daemon_arguments(parser)

    def __call__(self, args, app):
        _process_daemon(args)
        c = Client(app, Response)
        resp = c.get(args.url)
        print resp.data


class cmd_eurasia_server(object):
    """run application use eurasia http server"""
    def __init__(self, parser):
        add_host_arguments(parser)
        add_daemon_arguments(parser)

    def __call__(self, args, app):
        _process_daemon(args)
        try:
            from eurasia import WSGIServer
        except ImportError:
            print "You need to install eurasia"
            sys.exit()
        server = WSGIServer(app, bindAddress=(args.hostname, args.port))
        server.run()


class cmd_eventlet_server(object):
    """run application use eventlet http server"""
    def __init__(self, parser):
        add_host_arguments(parser)
        parser.add_argument("--debug", default=False, action="store_true", dest="debug")
        add_daemon_arguments(parser)

    def __call__(self, args, app):
        _process_daemon(args)
        try:
            import eventlet
            from eventlet import wsgi
        except ImportError:
            print "You need to install eventlet"
            sys.exit()
        if args.debug:
            log = None
        else:
            log = nul
        wsgi.server(eventlet.listen((args.hostname, args.port)), app, log=log)


class cmd_gevent_server(object):
    """run application use gevent wsgi server"""
    def __init__(self, parser):
        add_host_arguments(parser)
        parser.add_argument("--monkey", default=False, action="store_true", dest="monkey")
        add_daemon_arguments(parser)

    def __call__(self, args, app):
        _process_daemon(args)
        try:
            from gevent.wsgi import WSGIServer
        except ImportError:
            print "You need to install gevent"
            sys.exit()
        if args.monkey:
            from gevent import monkey
            monkey.patch_all()
        WSGIServer((args.hostname, args.port), app).serve_forever()


class cmd_twisted_server(object):
    """run application use twisted http server"""
    def __init__(self, parser):
        add_host_arguments(parser)
        parser.add_argument("-r", default="0", type=int, metavar="Reactor Type", dest="reactor_type", help="""[default 0]
            1       epoll   reactor
            2       poll    reactor
            3       kqueue  reactor
            4       iocp    reactor
            other   select  reactor""")
        add_daemon_arguments(parser)

    def __call__(self, args, app):
        _process_daemon(args)
        reactor_type = args.reactor_type
        try:
            if reactor_type == 1:
                from twisted.internet import epollreactor
                epollreactor.install()
            elif reactor_type == 2:
                from twisted.internet import pollreactor
                pollreactor.install()
            elif reactor_type == 3:
                from twisted.internet import kqreactor
                kqreactor.install()
            elif reactor_type == 4:
                from twisted.internet import iocpreactor
                iocpreactor.install()
            from twisted.internet import reactor
            from twisted.application import service, strports
            from twisted.web2 import server, channel, wsgi
        except ImportError:
            print "You need to install twisted and twisted.web2"
            sys.exit()

        application = service.Application('web2-wsgi') # call this anything you like
        site = server.Site(wsgi.WSGIResource(app))
        s = strports.service('tcp:%d'%args.port, channel.HTTPFactory(site))
        s.setServiceParent(application)
        s.startService()
        reactor.run()


class cmd_tornado_server(object):
    """run application use tornado http server"""
    def __init__(self, parser):
        add_host_arguments(parser)
        add_daemon_arguments(parser)

    def __call__(self, args, application):
        _process_daemon(args)
        from tornado.wsgi import WSGIContainer
        from tornado.httpserver import HTTPServer
        from tornado.ioloop import IOLoop
        container = WSGIContainer(application)
        server = HTTPServer(container)
        server.listen(args.port)
        try:
            IOLoop.instance().start()
        except KeyboardInterrupt:
            IOLoop.instance().stop()


class cmd_shell(object):
    """Start a new interactive python session."""
    def __init__(self, parser):
        parser.add_argument("--no-ipython", default=True, action="store_false", dest="ipython")

    def __call__(self, args, app):
        banner = 'Interactive Accost Shell'
        namespace = dict(app=app)
        if args.ipython:
            try:
                try:
                    from IPython.frontend.terminal.embed import InteractiveShellEmbed
                    sh = InteractiveShellEmbed(banner1=banner)
                except ImportError:
                    from IPython.Shell import IPShellEmbed
                    sh = IPShellEmbed(banner=banner)
            except ImportError:
                pass
            else:
                sh(local_ns=namespace)
                return
        from code import interact
        interact(banner, local=namespace)


class cmd_runserver(object):
    """Start a new development server."""
    def __init__(self, parser):
        add_host_arguments(parser)

    def __call__(self, args, app):
        from werkzeug.serving import run_simple
        run_simple(args.hostname, args.port, app, use_reloader=True, use_debugger=True,
                   use_evalex=True, threaded=False, processes=1)


class cmd_winsvc(object):
    """handler windows service"""
    def __init__(self, parser):
        pass

    def __call__(self, args, app):
        import win32serviceutil
        import win32service
        import win32event
        from .wsgiserver import CherryPyWSGIServer

        class Service(win32serviceutil.ServiceFramework):
            _svc_name_ = "accost_service"
            _svc_display_name_ = "Accost Service"
            def __init__(self, args):
                win32serviceutil.ServiceFramework.__init__(self, args)
                self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

            def SvcDoRun(self):
                self.server = CherryPyWSGIServer(('0.0.0.0', 80), app, 50)
                self.server.start()
                # 等待服务被停止
                win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)

            def SvcStop(self):
                # 先告诉SCM停止这个过程
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                # 停止服务
                self.server.stop()
                # 设置事件
                win32event.SetEvent(self.hWaitStop)

        win32serviceutil.HandleCommandLine(Service, argv=sys.argv[1:])


class cmd_profile(object):
    """Start a new development server."""
    def __init__(self, parser):
        add_host_arguments(parser)

    def __call__(self, args, app):
        from werkzeug.serving import run_simple
        from werkzeug.contrib.profiler import ProfilerMiddleware
        app = ProfilerMiddleware(app, None, ('time', 'calls'), ())
        run_simple(args.hostname, args.port, app, False, None, False, 1)


class cmd_show_urls(object):
    """display url structure"""
    def __init__(self, parser):
        pass

    def __call__(self, args, app):
        for rule in sorted(app.accost_application.url_mapping.iter_rules(), key=lambda r: str(r)):
            print "%-30s"%rule, rule.endpoint


class cmd_dbshell(object):
    """run mysql client use database configuration of the application"""
    def __init__(self, parser):
        pass

    def __call__(self, args, app):
        db = app.accost_application.db
        if len(db) == 0:
            sys.exit('database not found')
        elif len(db) == 1:
            db.popitem()[1].runDBShell()
        else:
            print 'There are %d databases, please choose one of them:'%len(db.items())
            name = input(', '.join([dbname for dbname in db]))
            if not name in db:
                sys.exit('no such database')
            db[name].runDBShell()


class cmd_show_tables(object):
    """show tables of all application databases"""
    def __init__(self, parser):
        pass

    def __call__(self, args, app):
        for dbname, store in app.accost_application.db.items():
            print '[%s]'%dbname
            store.showTables()
        engine = getattr(app.accost_application.app, "engine")
        if not engine: return
        conn = engine.connect()
        for r in conn.execute("SHOW TABLES").fetchall():
            print r[0]


class cmd_show_create(object):
    """show create table sql of all application databases"""
    def __init__(self, parser):
        pass

    def __call__(self, args, app):
        for dbname, store in app.accost_application.db.items():
            print '[%s]'%dbname
            store.showCreate()
        engine = getattr(app.accost_application.app, "engine")
        if not engine: return
        conn = engine.connect()
        for r in conn.execute("SHOW TABLES").fetchall():
            for line in conn.execute("SHOW CREATE TABLE `%s`"%r[0]):
                print line[1]
                print


class cmd_initd_cgi(object):
    def __init__(self, parser):
        parser.add_argument("-p", "--port", default=7000, type=int, metavar="PORT", dest="port", help="server port")
        parser.add_argument("--protocol", default="scgi", choices=["scgi", "fcgi", "ajp"], metavar="PROTOCOL", dest="protocol", help="scgi, fcgi or ajp(default scgi)")
        parser.add_argument("--method", default="threaded", choices=["threaded", "fork"], metavar="METHOD", dest="method", help="threaded or fork(default threaded)")
        parser.add_argument("--children", default=50, type=int, metavar="NUM", dest="children", help="number of threads or processes(default=50)")

    def __call__(self, args, app):
        from .initproject import env
        tmpl = env.get_template("cgi_initd.py")
        print tmpl.render(
                pythonbin = os.path.abspath(sys.executable),
                argv0 = os.path.abspath(sys.argv[0]),
                pname = app.accost_application.app.__name__.lower(),
                args = args,
        )


class cmd_initd_uwsgi(object):
    def __init__(self, parser):
        parser.add_argument("-p", "--port", default=7000, type=int, metavar="PORT", dest="port", help="server port")

    def __call__(self, args, app):
        import multiprocessing
        from .initproject import env
        tmpl = env.get_template("uwsgi_initd.py")
        print tmpl.render(
                pythonbin = os.path.abspath(sys.executable),
                argv0 = os.path.abspath(sys.argv[0]),
                appdir = os.path.abspath(os.path.dirname(sys.argv[0])),
                pname = app.accost_application.app.__name__.lower(),
                args = args,
                num_workers = 2 * multiprocessing.cpu_count() + 1,
        )


class Command(object):
    def __init__(self, app):
        self.app = app
        self.application = application = make_application(app)
        self._actions = [
                ('runcgi'   ,   cmd_runcgi),
                ('shell'    ,   cmd_shell),
                ('runserver',   cmd_runserver),
                ('cherrypy' ,   cmd_cherrypy),
                ('asynwsgi' ,   cmd_asynwsgi),
                ('eurasia'  ,   cmd_eurasia_server),
                ('eventlet' ,   cmd_eventlet_server),
                ('gevent'   ,   cmd_gevent_server),
                ('twisted'  ,   cmd_twisted_server),
                ('tornado'  ,   cmd_tornado_server),
                ('profile'  ,   cmd_profile),
                ('get'      ,   cmd_httpget),
                ('clean'    ,   cmd_clean),
                ('winsvc'   ,   cmd_winsvc),
                ('bshell'   ,   cmd_bshell),
                ('urls'     ,   cmd_show_urls),
                ('dbshell'  ,   cmd_dbshell),
                ('showcreate',  cmd_show_create),
                ('showtables',  cmd_show_tables),
                ('initd_cgi',   cmd_initd_cgi),
                ('initd_uwsgi', cmd_initd_uwsgi),
        ]
        import argparse
        self.parser = argparse.ArgumentParser(description='Command Line Operations', add_help=True)
        self.subparsers = self.parser.add_subparsers(help='Sub Command Help')

    def subcmd_help(self, args, app):
        cmd = args.cmd[0]
        if cmd in self.subparsers.choices:
            self.subparsers.choices[cmd].print_help()
        else:
            print "invalid command: %s"%cmd

    def run(self):
        for name, action in self._actions:
            self.register(action, name)
        p = self.subparsers.add_parser("help", add_help=False, help="show command help document")
        p.add_argument("cmd", nargs=1, metavar="COMMAND")
        p.set_defaults(func=self.subcmd_help)
        args = self.parser.parse_args()
        if hasattr(args, "func"):
            args.func(args, self.application)

    def register(self, cls, name=None):
        name = name if name is not None else cls.__class__.__name__
        subparser = self.subparsers.add_parser(name, add_help=True, help=cls.__doc__)
        obj = cls(subparser)
        subparser.set_defaults(func=obj)
